namespace TEXASPOKER {

using origin_t = pair<int, int>; // 花色, 点数

int const DIAMOND = 1;
int const CLUB = 2;
int const HEART = 3;
int const SPADE = 4;

/// 牌型从小到大
int const SANPAI = 1; 
int const DUIZI = 2;
int const LIANGDUI = 3;
int const SANTIAO = 4;
int const SHUNZI = 5;
int const TONGHUA = 6;
int const HULU = 7;
int const SITIAO = 8;
int const TONGHUASHUN = 9;
int const HUANGJIA = 10;

int f(char ch){
    switch(ch){
        case 'H': return HEART;
        case 'S': return SPADE;
        case 'D': return DIAMOND;
        case 'C': return CLUB;
    }
    assert(0);
    return 0;
}

int g(char ch){
    if(isdigit(ch)) return ch - '0';
    if('A' == ch) return 14;
    if('K' == ch) return 13;
    if('Q' == ch) return 12;
    if('J' == ch) return 11;
    if('T' == ch) return 10;
    assert(0);
    return 0;
}

struct _t{
    int type;
    vector<int> seq;
    _t():type(0),seq(){}
    _t(int a, const vector<int> & b):type(a),seq(b){}

    static const _t & none(){
        static const _t NONE;
        return NONE;
    }

    bool operator < (const _t & right) const {
        if(type != right.type) return type < right.type;
        assert(this->seq.size() == right.seq.size());
        auto sz = this->seq.size();
        for(int i=0;i<sz;++i){
            if(seq[i] != right.seq[i]) return seq[i] < right.seq[i];
        }
        return false;
    }

    bool operator == (const _t & right) const {
        if(type != right.type) return false;
        assert(this->seq.size() == right.seq.size());
        auto sz = this->seq.size();
        for(int i=0;i<sz;++i)if(seq[i] != right.seq[i])return false;
        return true;
    }
};

_t getHuangjia(const vector<origin_t> & vec){
    array<vector<int>, 5> arr;
    for(const auto & p : vec){
        arr[p.first].emplace_back(p.second);
    }
    for(auto & a : arr){
        if(5 == a.size()){
            sort(a.begin(), a.end());
            if(14 == a[4] and 13 == a[3] and 12 == a[2] and 11 == a[1] and 10 == a[0]){
                return {HUANGJIA, {}};
            }
        }
    }
    return _t::none();
}

_t getTonghuashun(const vector<origin_t> & vec){
    array<vector<int>, 5> arr;
    for(const auto & p : vec){
        arr[p.first].emplace_back(p.second);
    }
    for(auto & a : arr){
        if(5 == a.size()){
            sort(a.begin(), a.end());
            if(a[0] + 1 == a[1] and a[1] + 1 == a[2] and a[2] + 1 == a[3]){
                if(a[3] + 1 == a[4]){
                    return {TONGHUASHUN, {a[0]}};
                }
                if(a[4] == 14 and a[0] == 2){
                    return {TONGHUASHUN, {1}};
                }
            }
        }
    }
    return _t::none();    
}

_t getSitiao(const vector<origin_t> & vec){
    int cnt[15] = {0};
    for(const auto & p : vec){
        if(4 == (cnt[p.second] += 1)){
            return {SITIAO, {p.second}};
        }
    }
    return _t::none();
}

_t getHulu(const vector<origin_t> & vec){
    int cnt[15] = {0};
    for(const auto & p : vec){
        cnt[p.second] += 1;
    }   
    int u = 0, v = 0; 
    for(int i=2;i<=14;++i){
        if(3 == cnt[i]){
            assert(0 == u);
            u = i;
        }else if(2 == cnt[i]){
            v = i;
        }
    }
    if(u and v){
        return {HULU, {u, v}};
    }
    return _t::none();
}

_t getTonghua(const vector<origin_t> & vec){
    array<vector<int>, 5> arr;
    for(const auto & p : vec){
        arr[p.first].emplace_back(p.second);
    }    
    for(auto & a : arr){
        if(5 == a.size()){
            sort(a.begin(), a.end(), greater<int>());
            return {TONGHUA, a};
        }
    }
    return _t::none();
}

_t getShunzi(const vector<origin_t> & vec){
    vector<int> arr;
    for(const auto & p : vec) arr.emplace_back(p.second);
    sort(arr.begin(), arr.end());

    if(arr[0] + 1 == arr[1] and arr[1] + 1 == arr[2] and arr[2] + 1 == arr[3]){
        if(arr[3] + 1 == arr[4]){
            return {SHUNZI, {arr[0]}};
        }
        if(14 == arr[4] and 2 == arr[0]){
            return {SHUNZI, {1}};
        }
    }
    return _t::none();
}

_t getSantiao(const vector<origin_t> & vec){
    int cnt[15] = {0};
    for(const auto & p : vec){
        cnt[p.second] += 1;
    }   
    int u = 0, v = 0, w = 0;
    for(int i=2;i<=14;++i){
        if(3 == cnt[i]){
            assert(0 == u);
            u = i;
        }else if(1 == cnt[i]){
            if(v){
                w = i;
            }else{
                v = i;
            }
        }
    }        
    if(u){
        assert(w and v and w != v);
        return {SANTIAO, {u, w, v}};
    }
    return _t::none();
}

_t getLiangdui(const vector<origin_t> & vec){
    int cnt[15] = {0};
    for(const auto & p : vec){
        cnt[p.second] += 1;
    }   
    int u = 0, v = 0, w = 0;    
    for(int i=2;i<=14;++i){
        if(2 == cnt[i]){
            if(u){
                v = i;
            }else{
                u = i;
            }
        }else if(1 == cnt[i]){
            w = i;
        }
    }  
    if(u and v){
        assert(v > u and w);
        return {LIANGDUI, {v, u, w}};
    }
    return _t::none();
}

_t getDuizi(const vector<origin_t> & vec){
    int cnt[15] = {0};
    for(const auto & p : vec){
        cnt[p.second] += 1;
    } 
    int u = 0;
    vector<int> v;
    for(int i=2;i<=14;++i){
        if(2 == cnt[i]){
            assert(0 == u);
            u = i;
        }else if(1 == cnt[i]){
            v.emplace_back(i);
        }
    }
    if(u){
        assert(3 == v.size());
        return {DUIZI, {u, v[2], v[1], v[0]}};
    }
    return _t::none();
}

_t getSanpai(const vector<origin_t> & vec){
    int cnt[15] = {0};
    int cc[5] = {0};
    for(const auto & p : vec){
        cnt[p.second] += 1;
        cc[p.first] += 1;
    } 
    for(int i=1;i<=4;++i)assert(5 != cc[i]);
    vector<int> v;
    for(int i=2;i<=14;++i){
        if(1 == cnt[i]){
            v.emplace_back(i);
        }
    }
    assert(5 == v.size());
    return {SANPAI, {v[4], v[3], v[2], v[1], v[0]}};  
    return _t::none();
}

/// 返回当前5张牌的牌型
_t get(const vector<origin_t> & vec){
    assert(vec.size() == 5);
    _t ret;
    
    ret = getHuangjia(vec);
    if(ret.type) return ret;

    ret = getTonghuashun(vec);
    if(ret.type) return ret;

    ret = getSitiao(vec);
    if(ret.type) return ret;

    ret = getHulu(vec);
    if(ret.type) return ret;

    ret = getTonghua(vec);
    if(ret.type) return ret;

    ret = getShunzi(vec);
    if(ret.type) return ret;

    ret = getSantiao(vec);
    if(ret.type) return ret;

    ret = getLiangdui(vec);
    if(ret.type) return ret;

    ret = getDuizi(vec);
    if(ret.type) return ret;

    ret = getSanpai(vec);
    if(ret.type) return ret;

    assert(0);
    return _t::none();
}

/// 返回5张公共牌2张手牌能够构成的最大牌型
_t get(const vector<origin_t> & gong, const vector<origin_t> & shou){
    assert(5 == gong.size());
    assert(2 == shou.size());
 
    _t ans = get(gong);
	for(int i=0;i<2;++i){
        _t tmp = get({gong[1], gong[2], gong[3], gong[4], shou[i]});
		if(ans < tmp) ans = tmp;

		tmp = get({gong[0], gong[2], gong[3], gong[4], shou[i]});
		if(ans < tmp) ans = tmp;

		tmp = get({gong[0], gong[1], gong[3], gong[4], shou[i]});
		if(ans < tmp) ans = tmp;

		tmp = get({gong[0], gong[1], gong[2], gong[4], shou[i]});
		if(ans < tmp) ans = tmp;

		tmp = get({gong[0], gong[1], gong[2], gong[3], shou[i]});
		if(ans < tmp) ans = tmp;
	}

    vector<origin_t> candi(shou.begin(), shou.end());
    for(int i=0,n=gong.size();i<n;++i){
        candi.emplace_back(gong[i]);
        for(int j=i+1;j<n;++j){
            candi.emplace_back(gong[j]);
            for(int k=j+1;k<n;++k){
                candi.emplace_back(gong[k]);
                _t tmp = get(candi);
                candi.pop_back();

                if(ans < tmp) ans = tmp;
            }
            candi.pop_back();
        }
        candi.pop_back();
    }
    return ans;
}

}